/*
    Project:        Home Power Control
    File:           HPCLogger.h
    Author:         based on Logger from Claudio Scordino (http://retis.sssup.it/~scordino/code/logger.html)
                    redesign: christian@karunamusic.at
    Date:           12.2014
    Description:    Basic implementation of a logger-class to send logmessages with various loglevels
                    via signals to all connected subscribers.
 */

#ifndef HPCLOGGER_H
#define HPCLOGGER_H

#include <fstream>
#include <ostream>
#include <string>
#include <sstream>
#include <sys/time.h>
#include <boost/signals2.hpp>

/**< Comment this line if you don't need multithread support. */
#define LOGGER_MULTITHREAD


#ifdef LOGGER_MULTITHREAD
#include <pthread.h>
#endif


/**
 * \brief Macro to print log messages.
 * Example of usage of the HPCLogger:
 *	    LOG(info, "hello " << "world");
 */
#define LOG(l,m) \
{ \
	std::ostringstream __debug_stream__; \
	__debug_stream__ << m; \
	HPCLogger::getInstance().print(l,BOOST_CURRENT_FUNCTION,__FILE__,__LINE__,__debug_stream__.str()); \
}

/**
 * \brief Macro to print log messages (level 'trace') and throw an exception with the same message.
 * Example of usage of the HPCLogger:
 *	    THROW("hello " << "world");
 */
#define THROW(m) \
{ \
	std::ostringstream __debug_stream__; \
	__debug_stream__ << m; \
	HPCLogger::getInstance().print(trace,BOOST_CURRENT_FUNCTION,__FILE__,__LINE__,__debug_stream__.str()); \
	throw std::runtime_error( __debug_stream__.str() ); \
}

/**< Enum with loglevels. */
typedef enum
{
    fatal,
    error,
    warning,
    info,
    debug,
    trace
} TLogLevel;


/** \brief Logmessage to send vai signal to connected subscribers.
 */
class HPCLogMessage
{
public:
    /** \brief Constructor with all arguments from HPCLogger::print.
     *
     * \param loglevel the loglevel - see enum above.
     * \param method methodname - set with BOOST_CURRENT_FUNCTION in the LOG macro.
     * \param sourceFile the filename - set with __FILE__ in the LOG macro.
     * \param codeLine the linenumber - set with __LINE__ in the LOG macro.
     * \param message the string with the logmessage.
     *
     */
    HPCLogMessage(  const TLogLevel	    logLevel,
                    const std::string&  method,
                    const std::string&	sourceFile,
                    const unsigned int 	codeLine,
                    const std::string& 	message );
    /** \brief Destructor.
     */
    virtual ~HPCLogMessage(){}

    /**< get the loglevel */
    inline TLogLevel LogLevel() const
        { return m_LogLevel; }
    /**< get the timestamp */
    inline std::string TimeStamp() const
        { return m_TimeStamp; }
    /**< get the methodname */
    inline std::string Method() const
        { return m_Method; }
    /**< get the sourcefilename */
    inline std::string SourceFile() const
        { return m_SourceFile; }
    /**< get the linenumber in the file */
    inline unsigned int CodeLine() const
        { return m_CodeLine; }
    /**< get the logmessage */
    inline std::string Message() const
        { return m_Message; }

private:
    /** \brief get the timestamp.
     *
     * \return string with the timestamp.
     *
     */
    const std::string dateTime();

    TLogLevel    m_LogLevel;
    std::string  m_TimeStamp;
    std::string  m_Method;
    std::string  m_SourceFile;
    unsigned int m_CodeLine;
    std::string  m_Message;
};

/** \brief Operator to stream the logmessage to console or file.
 *
 * \param dest the ostream.
 * \param msg the HPCLogMessage.
 * \return the ostream.
 *
 */
std::ostream& operator << ( std::ostream& dest, const HPCLogMessage& msg );

/**
 * \brief Simple logger to log messages on file and console.
 * This is the implementation of a simple logger in C++. It is implemented
 * as a Singleton, so it can be easily called through two DEBUG macros.
 * It is Pthread-safe.
 * It allows to log on both file and screen, and to specify a verbosity
 * threshold for both of them.
 */
class HPCLogger
{
public:
    /** \brief Typedefinition of the callbackmethod for logmessages.
     *
     * \param msg HPCLogMessage.
     * \return nothing.
     *
     */
    typedef boost::signals2::signal<void(const HPCLogMessage &)> signal_t;
    /** \brief A connection type.
     */
    typedef boost::signals2::connection connection_t;

    /** \brief Get the static instance of the logger.
     *
     * \return HPCLogger&
     *
     */
    static HPCLogger& getInstance();

    /** \brief send an event with the logmessage to all subscribed targets.
     *
     * \param loglevel the loglevel - see enum above.
     * \param method methodname - set with BOOST_CURRENT_FUNCTION in the LOG macro.
     * \param sourceFile the filename - set with __FILE__ in the LOG macro.
     * \param codeLine the linenumber - set with __LINE__ in the LOG macro.
     * \param message the string with the logmessage.
     *
     */
    void print( const TLogLevel	    logLevel,
                const std::string&	method,
                const std::string&	sourceFile,
                const unsigned int 	codeLine,
                const std::string& 	message );

    /** \brief Register eventhandler for the log events.
     *
     * \param subscriber method called if the value of the sensor has changed.
     * \return handle of the connection.
     *
     */
    connection_t Register( const signal_t::slot_type &subscriber );
    /** \brief Unregister the given connection handle.
     *
     * \param subscriber connection handle from the subscriber.
     *
     */
    void Unregister( connection_t subscriber );

private:
#ifdef LOGGER_MULTITHREAD
    static pthread_mutex_t m_Lock; /**< Lock for mutual exclusion between different threads. */
#endif

    static HPCLogger* m_Log; /**< Pointer to the unique HPCLogger. */

    boost::signals2::connection m_EventHandler; /**< handle of the connected callback-method. */
    signal_t m_LogEvent; /**< the callback-method for log events. */

    HPCLogger(); /**< private constructor. */
    ~HPCLogger(); /**< private destructor. */

    /**
     * \brief Method to lock in case of multithreading
     */
    static void lock();

    /**
     * \brief Method to unlock in case of multithreading
     */
    static void unlock();
};

#endif // HPCLOGGER_H
